package com.me.web.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;


import java.util.ArrayList;
import java.util.List;


/**
 * 启动和管理功能。
 * 通过继承该抽象类，子类可以实现特定的服务器逻辑，如 TCP 服务器或 WebSocket 服务器。
 *2、 AbstractNettyServer 通过继承该抽象类，子类可以实现特定的服务器逻辑，TCP 服务器或 WebSocket 服务器。
 * @author chenlei
 */
@Slf4j
@Component
public abstract class AbstractNettyServer {


    /**
     * 存储绑定服务器的 ChannelFuture 对象列表，以便后续对服务器启动过程中的操作和状态进行管理
     */
    protected final List<ChannelFuture> channelFutures = new ArrayList<>();


    /**
     * 启动 Netty 服务器的方法
     */
    public void start() {

        // 创建一个 NioEventLoopGroup 作为服务器的 boss 线程组，用于接收客户端的连接请求
        EventLoopGroup boss = new NioEventLoopGroup();
        // 创建一个 NioEventLoopGroup 作为服务器的 worker 线程组，用于处理客户端的业务逻辑
        EventLoopGroup worker = new NioEventLoopGroup();


        // 创建一个 ServerBootstrap 实例，用于配置和启动 Netty 服务器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(boss, worker)
                // 设置服务器的通道类型为 NioServerSocketChannel，即使用 NIO 进行网络通信
                .channel(NioServerSocketChannel.class)
                // 设置服务器的 TCP 选项，SO_BACKLOG 表示服务器可接收的最大连接请求队列长度，用于处理大量并发连接时的请求堆积
                .option(ChannelOption.SO_BACKLOG, 128)
                // 设置子通道的 TCP 选项，SO_KEEPALIVE 表示启用 TCP 的 Keep-Alive 机制，保持长连接
                .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
                // 为子通道添加处理器，使用 ChannelInitializer 对每个新的 SocketChannel 进行初始化
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    // 初始化新的 SocketChannel 的处理器链
                    protected void initChannel(SocketChannel ch) {
                        // 调用抽象方法，由子类实现具体的通道处理器初始化逻辑
                        initChannelHandlers(ch);
                    }
                });

        //端口
        int port = getPort();
        //地址
        String host = getHost();
        try {
            // 尝试将服务器绑定到当前端口
            ChannelFuture cf = serverBootstrap.bind(port);
            // 为绑定操作添加监听器，监听绑定操作的成功和失败状态
            cf.addListener(future -> {
                if (future.isSuccess()) {
                    // 绑定成功时，记录日志，显示服务器绑定的端口和主机地址
                    log.info("服务器在端口 {} 上成功绑定，主机地址为 {}", host, getHost());
                } else {
                    // 绑定失败时，记录错误日志，显示绑定的端口和失败原因
                    log.error("绑定端口 {} 时发生连接错误: {}", host, future.cause().getMessage());
                }
            });
            // 将 ChannelFuture 添加到列表中，以便后续管理
            channelFutures.add(cf);
        } catch (Exception e) {
            // 处理绑定端口时发生的异常，记录错误日志
            log.error("绑定端口 {} 时发生连接错误: {}", port, e.getMessage());
        }


        // 遍历已绑定的 ChannelFuture 列表，等待通道关闭
        for (ChannelFuture cf : channelFutures) {
            try {
                cf.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                // 处理等待通道关闭时发生的异常，记录错误日志
                log.error("等待通道关闭时发生错误: {}", e.getMessage());
                // 中断当前线程状态，避免潜在的异常
                Thread.currentThread().interrupt();
            }
        }


        // 关闭 boss 线程组，释放资源
        boss.shutdownGracefully();
        // 关闭 worker 线程组，释放资源
        worker.shutdownGracefully();
    }


    /**
     * 获取服务器的主机地址，由子类实现具体逻辑
     *
     * @return 服务器的主机地址
     */
    protected abstract String getHost();


    /**
     * 获取服务器的端口范围，由子类实现具体逻辑
     *
     * @return 服务器的端口范围，多个端口使用逗号分隔
     */
    protected abstract int getPort();


    /**
     * 初始化通道处理器，由子类实现具体逻辑，根据不同的服务器类型添加不同的处理器
     *
     * @param ch 要初始化的 SocketChannel
     */
    protected abstract void initChannelHandlers(SocketChannel ch);
}
